﻿//Created: 3/14/2013 @ 10:11pm by Dagradt
//Last changed date: 3/15/2013 @ 7:43pm by Dagradt

using Action = Styx.TreeSharp.Action;
using Styx;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.TreeSharp;
using Styx.WoWInternals.WoWObjects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Styx.WoWInternals;
using Styx.Common;

namespace Death_Knight_Unholy_
{
    public class Main : CombatRoutine
    {
        public override string Name { get { return "Death Knight(Unholy)"; } }
        public override WoWClass Class { get { return WoWClass.DeathKnight; } }
        LocalPlayer Me { get { return StyxWoW.Me; } }

        private Composite _preCombatBuffs, _combatBuffs, _Heal, _Combat;
        public override Composite PreCombatBuffBehavior { get { return _preCombatBuffs; } }
        public override Composite CombatBuffBehavior { get { return _combatBuffs; } }
        public override Composite HealBehavior { get { return _Heal; } }
        public override Composite CombatBehavior { get { using (StyxWoW.Memory.AcquireFrame()) return _Combat; } }

        #region Initialize
        public override void Initialize()
        {
            _preCombatBuffs = createPreCombatBuffs();
            _combatBuffs = createCombatBuffs();
            _Heal = createHeal();
            _Combat = createCombat();
        }
        #endregion

        #region Pre-Combat Buffs
        Composite createPreCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        //0 	0.00 	flask,type=winters_bite
                        //1 	0.00 	food,type=black_pepper_ribs_and_shrimp
                        //unholy_presence
                        Cast(48265, ret => !buffExists(48265, Me)),
                        //horn_of_winter
                        Cast(57330, ret => !buffExists(57330, Me)),
                        //5 	0.00 	army_of_the_dead
                        //6 	0.00 	mogu_power_potion
                        //7 	0.00 	blood_fury
                        //raise_dead
                        Cast(46584, ret => !Me.GotAlivePet),
                        Cast(45524, ret => gotTarget && targetDistance(Me.CurrentTarget) > 5 * 5 && targetDistance(Me.CurrentTarget) <= 30 * 30 && !debuffExists(45524, Me.CurrentTarget))
                        ));
        }
        #endregion

        #region Combat Buffs
        Composite createCombatBuffs()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        Cast(45524, ret => gotTarget && targetDistance(Me.CurrentTarget) > 5 * 5 && targetDistance(Me.CurrentTarget) <= 30 * 30 && !debuffExists(45524, Me.CurrentTarget)),
                        Cast(73975, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && deathRunesActive > 0 && NecroStrike)
                        ));
        }
        #endregion

        #region Heal
        Composite createHeal()
        {
            return
                new Decorator(ret => true,
                    new PrioritySelector(
                        ));
        }
        #endregion

        #region Combat
        Composite createCombat()
        {
            return
                new Decorator(ret => Manual,
                    new PrioritySelector(
                        //9 	1.00 	auto_attack
                        //A 	3.33 	blood_fury,if=time>=2
                        //B 	2.62 	outbreak,if=stat.attack_power>(dot.blood_plague.attack_power+5000)&time>15&!(cooldown.unholy_blight.remains>49)
                        //C 	5.59 	plague_strike,if=stat.attack_power>(dot.blood_plague.attack_power+5000)&time>15&!(cooldown.unholy_blight.remains>49)
                        //D 	1.00 	mogu_power_potion,if=buff.dark_transformation.up&target.time_to_die<=35
                        //E 	3.00 	unholy_frenzy,if=time>=4
                        //F 	7.89 	use_item,name=gauntlets_of_the_allconsuming_maw,if=time>=4
                        //blood_tap,if=talent.blood_tap.enabled&buff.blood_charge.stack>10&runic_power>=32
                        Cast(45529, ret => buffStackExists(114851, Me) > 10 && runicPower >= 32),
                        //H 	3.73 	unholy_blight,if=talent.unholy_blight.enabled&(dot.frost_fever.remains<3|dot.blood_plague.remains<3)
                        //outbreak,if=dot.frost_fever.remains<3|dot.blood_plague.remains<3
                        Cast(77575, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && (debuffTimeLeft(55095, Me.CurrentTarget) < 3000 || debuffTimeLeft(55078, Me.CurrentTarget) < 3000)),
                        //J 	31.50 	soul_reaper,if=target.health.pct-3*(target.health.pct%target.time_to_die)<=45
                        //K 	11.92 	blood_tap,if=talent.blood_tap.enabled&(target.health.pct-3*(target.health.pct%target.time_to_die)<=45&cooldown.soul_reaper.remains=0)
                        //plague_strike,if=!dot.blood_plague.ticking|!dot.frost_fever.ticking
                        Cast(45462, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && (!debuffExists(55078, Me.CurrentTarget) || !debuffExists(55095, Me.CurrentTarget))),
                        //summon_gargoyle
                        Cast(49206, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && UseCDs),
                        //dark_transformation
                        Cast(63560, ret => buffStackExists(91342, Me) == 5),
                        //blood_tap,if=talent.blood_tap.enabled&buff.shadow_infusion.stack=5
                        Cast(45529, ret => buffStackExists(91342, Me) == 5),
                        //death_coil,if=runic_power>90
                        Cast(47541, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && runicPower > 90),
                        //death_and_decay,if=unholy=2
                        castOnTargetLocation(43265, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && unholyRunesActive == 2),
                        //blood_tap,if=talent.blood_tap.enabled&unholy=2&cooldown.death_and_decay.remains=0
                        Cast(45529, ret => unholyRunesActive == 2 && spellCooldownLeft(43265).TotalMilliseconds == 0),
                        //scourge_strike,if=unholy=2
                        Cast(55090, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && unholyRunesActive == 2),
                        //festering_strike,if=blood=2&frost=2
                        Cast(85948, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && bloodRunesActive == 2 && frostRunesActive == 2),
                        //death_and_decay
                        castOnTargetLocation(43265, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30),
                        //blood_tap,if=talent.blood_tap.enabled&cooldown.death_and_decay.remains=0
                        Cast(45529, ret => spellCooldownLeft(43265).TotalMilliseconds == 0),
                        //death_coil,if=buff.sudden_doom.react|(buff.dark_transformation.down&rune.unholy<=1)
                        Cast(47541, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && (buffExists(81340, Me) || (!buffExists(63560, Me.Pet) && unholyRunesActive <= 1))),
                        //scourge_strike
                        Cast(55090, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && !NecroStrike),
                        Cast(55090, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && unholyRunesActive > 0),
                        //Y 	0.00 	plague_leech,if=talent.plague_leech.enabled&cooldown.outbreak.remains<1
                        //festering_strike
                        Cast(85948, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && !NecroStrike),
                        Cast(85948, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 5 * 5 && bloodRunesActive > 0 && frostRunesActive > 0),
                        //horn_of_winter
                        Cast(57330, ret => true),
                        //death_coil,if=buff.dark_transformation.down|(cooldown.summon_gargoyle.remains>8&buff.dark_transformation.remains>8)
                        Cast(47541, ret => gotTarget && targetDistance(Me.CurrentTarget) <= 30 * 30 && (!buffExists(63560, Me.Pet) || (spellCooldownLeft(49206).TotalMilliseconds > 8000 && buffTimeLeft(63560, Me.Pet) > 8000))),
                        //blood_tap,if=talent.blood_tap.enabled&buff.blood_charge.stack>=8
                        Cast(45529, ret => buffStackExists(114851, Me) >= 8),
                        //empower_rune_weapon
                        Cast(47568, ret => UseCDs)
                        ));
        }
        #endregion

        #region Methods

        #region Buff Checks
        public bool buffExists(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        public uint buffStackExists(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                {
                    if (Results.StackCount > 0)
                        return Results.StackCount;
                }
            }
            return 0;
        }
        public double buffTimeLeft(int Buff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Buff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        #endregion

        #region Cooldown Check
        protected TimeSpan spellCooldownLeft(int Spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(Spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Original.CooldownTimeLeft;
            }
            return TimeSpan.MaxValue;
        }
        #endregion

        #region Debuff Checks
        public bool debuffExists(int Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Debuff);
                if (Results != null)
                    return true;
            }
            return false;
        }
        public double debuffTimeLeft(int Debuff, WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.GetAuraById(Debuff);
                if (Results != null)
                {
                    if (Results.TimeLeft.TotalMilliseconds > 0)
                        return Results.TimeLeft.TotalMilliseconds;
                }
            }
            return 0;
        }
        #endregion

        #region Distance Check
        public double targetDistance(WoWUnit onTarget)
        {
            if (onTarget != null)
            {
                var Results = onTarget.DistanceSqr;
                if (Results != null)
                    return onTarget.DistanceSqr;
            }
            return 0;
        }
        #endregion

        #region Macro Checks
        public bool UseCDs { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return UseCDs and 0 or 1", 0)); } }
        public bool Manual { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return Manual and 0 or 1", 0)); } }
        public bool NecroStrike { get { return Convert.ToBoolean(Lua.GetReturnVal<int>("return NecroStrike and 0 or 1", 0)); } }
        #endregion

        #region Target Check
        public bool gotTarget { get { return Me.GotTarget && Me.CurrentTarget != null && Me.CurrentTarget.IsAlive; } } //<~ Check for Immune auras like the Paladin's Bubble or CC
        #endregion

        #region Rune Checks
        public int deathRunesActive { get { return Me.DeathRuneCount; } }
        public int bloodRunesActive { get { return Me.BloodRuneCount; } }
        public int frostRunesActive { get { return Me.FrostRuneCount; } }
        public int unholyRunesActive { get { return Me.UnholyRuneCount; } }
        #endregion

        #region Runic Power Check
        public double runicPower
        {
            get
            {
                try
                {
                    using (StyxWoW.Memory.AcquireFrame())
                        return Styx.WoWInternals.Lua.GetReturnVal<int>("return UnitPower(\"player\");", 0);
                }
                catch { return 0; }
            }
        }
        #endregion

        #region Spell Casting
        private delegate T Selection<out T>(object Context);
        Composite Cast(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            using (StyxWoW.Memory.AcquireFrame())
                return
                    new Decorator(ret =>
                        {
                            if (reqs != null && !reqs(ret))
                                return false;
                            if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                                return false;
                            return true;
                        },
                        new Action(ret =>
                            {
                                WoWSpell castingSpell = Me.CastingSpell;
                                if (castingSpell != null && castingSpell.Id != Spell)
                                    Lua.DoString("SpellStopCasting()");
                                Logging.Write("Casting: " + Spell);
                                SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                            })
                        );
        }
        Composite castOnTargetLocation(int Spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCanCast = false)
        {
            using (StyxWoW.Memory.AcquireFrame())
                return
                    new Decorator(ret =>
                        {
                            if (reqs != null && !reqs(ret))
                                return false;
                            if (!ignoreCanCast && !SpellManager.CanCast(Spell, onTarget != null ? onTarget(ret) : Me.CurrentTarget))
                                return false;
                            return true;
                        },
                        new Action(ret =>
                            {
                                WoWSpell castingSpell = Me.CastingSpell;
                                if (castingSpell != null && castingSpell.Id != Spell)
                                    Lua.DoString("SpellStopCasting()");
                                Logging.Write("Casting: " + Spell);
                                SpellManager.Cast(Spell, (onTarget != null ? onTarget(ret) : null));
                                SpellManager.ClickRemoteLocation(Me.CurrentTarget.Location);
                            })
                        );
        }
        #endregion

        #endregion
    }
}